Results presented in “Soil biodiversity and chemical stressors: global review and roadmap for future research”.

Figures are created from the script “FIGURES.R”.

Data

library(dplyr)
# load packages for maps
library(maptools)
library(ggmap)
library(rnaturalearth)
library(countrycode)
library(viridis)
library(ggplot2)
library(knitr)
library(patchwork)
library(reshape2)

`%notin%` = Negate(`%in%`)

# DATA--------
metadatreview <- read.csv("Data/References_pollution_review.csv")

How many studies included?

nrow(metadatreview)
[1] 274

1. Mapping knowledge gaps

1.1. Geographical bias

Are there geographical bias and what regions are poorly covered ?

Distribution of studies included by country

Distribution of studies included by country

Countries most represented?

stud <- metadatreview %>% 
  group_by(Country) %>% 
  summarize(no.stu = n())
Factor `Country` contains implicit NA, consider using `forcats::fct_explicit_na`
multicountrystud <- stud[grepl(";|,", stud$Country),]

# Manually add multiple countries studies:
addingcountries <- data.frame(rbind(c("Brazil", 1), c("Portugal", 1), 
                                    c("Canada", 1), c("france", 1), c("The Netherlands", 1), c("Switzerland", 1),
                                    c("Germany", 1), c("The Netherlands", 1), c("UK", 1), c("Portugal", 1),
                                    c("Indonesia", 1),c("Malaysia", 1),
                                    c("UK", 1),
                                    c("The Netherlands", 1), c("Belgium", 1), c("france", 1), c("germany", 1)))
colnames(addingcountries) <- colnames(stud)
addingcountries$no.stu <- as.numeric(addingcountries$no.stu)

stud <- rbind(as.data.frame(stud), addingcountries)

# harmonize country names
stud <- stud %>%
  mutate(iso =  countrycode(stud$Country, origin = "country.name", destination = "iso3c")) %>%
  dplyr::group_by(iso) %>%
  summarize(no.stud = sum(no.stu, na.rm = TRUE))
Some values were not matched unambiguously: , Brazil;Portugal, Canada;France;Netherlands;Switzerland, England, Germany, The Netherlands, UK, Portugal, Indonesia, Malaysia, Scotland, UK, The Netherlands; Belgium; France; Germany
Some strings were matched more than once, and therefore set to <NA> in the result: Brazil;Portugal,BRA,PRT; Canada;France;Netherlands;Switzerland,CAN,FRA,NLD,CHE; Germany, The Netherlands, UK, Portugal,DEU,NLD,PRT; Indonesia, Malaysia,IDN,MYS; The Netherlands; Belgium; France; Germany,BEL,FRA,DEU,NLD
stud[stud$no.stud > 20,]

1.2. Pollutant types

Time span of studies?

summary(metadatreview$Published.Year)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1991    2003    2009    2008    2014    2018 

Pollutants most/least represented?

whichPollutionType <- c("Metals", "PAH", "Pesticides", "Plastics/Plasticizers","Pharmaceuticals","Radiation", "Nanoparticles", "Others")

iterations  <-  length(whichPollutionType)
variables <-  1


output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichPollutionType[i], ignore.case = TRUE, as.character(metadatreview$PollutantType)))
}

output <- data.frame(PollutionType = whichPollutionType,
                     no.studies = output)

kable(output)

PollutionType no.studies
Metals 159
PAH 19
Pesticides 89
Plastics/Plasticizers 2
Pharmaceuticals 14
Radiation 3
Nanoparticles 2
Others 7

NA

What are the human activities responsible for soil pollution that are the most commonly studied ?

whichPollutionSource <- c("Agricultural/livestock","Industrial","Mining/Smelting","Military/Wars",                        "Natural/geogenic","Waste/Sewage","Urban/Transport","Others")

iterations  <-  length(whichPollutionSource)
variables <-  1


output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
   output[i,] <- length(grep(whichPollutionSource[i], ignore.case = TRUE, as.character(metadatreview$PollutionSource)))
}

output <- data.frame(output)
output$PollutionSource <- whichPollutionSource

kable(output)

output PollutionSource
100 Agricultural/livestock
31 Industrial
73 Mining/Smelting
9 Military/Wars
3 Natural/geogenic
23 Waste/Sewage
18 Urban/Transport
37 Others

NA
# sum up industrial/mining/waste (overlaps)
sum(output$output[output$PollutionSource=="Industrial"],
    output$output[output$PollutionSource=="Mining/Smelting"])
[1] 104

What are the “others” in Pollutant type category: - salts - explosives

metadatreview$PollutantName[metadatreview$PollutantType=="Others"]
[1] salt: chloride and sodium salt                     
[3] chemical warfare agents  
195 Levels:  ...

Mixture of chemicals: what scenarios studied?

# create new pol type where = mixture when more than one category of pollutants are involved
metadatreview$PollutantType2 <- as.factor(ifelse(grepl(",", metadatreview$PollutantType), "Mixture",                                    as.character(metadatreview$PollutantType)))

summary(metadatreview$PollutantType2)
                                Metals 
                                   143 
                               Mixture 
                                    17 
                         Nanoparticles 
                                     2 
                                Others 
                                     3 
PAH (Polycyclic aromatic hydrocarbons) 
                                     8 
                            Pesticides 
                                    85 
                       Pharmaceuticals 
                                    13 
               Radionuclides/Radiation 
                                     3 

When accounting for mixture across AND within pollutant categories: most studies focus on multiple chemicals. Indeed, half of the studies are observational, and most contaminations and pollutions involve multiple chemicals in real-world scenarios.

# create new pol type where = mixture when more than one category of pollutant is involved (for plot purposes)
metadatreview$PollutantType3 <-as.factor(ifelse(
  # mixtures across and within categoreis of stressors
  grepl(",|;|and|several|many|multiple|nanomaterials|metals|cides|carbons|PAHs|agents|residues", metadatreview$PollutantName)|
    grepl(",", metadatreview$PollutantType), "Mixtures", 
  as.character(metadatreview$PollutantType)))

summary(metadatreview$PollutantType3)
                                Metals 
                                    34 
                              Mixtures 
                                   185 
                         Nanoparticles 
                                     1 
                                Others 
                                     1 
PAH (Polycyclic aromatic hydrocarbons) 
                                     3 
                            Pesticides 
                                    40 
                       Pharmaceuticals 
                                     7 
               Radionuclides/Radiation 
                                     3 

1.3. Taxonomic groups

What are the taxa most represented by the studies?

whichTaxa <- c("Protists","Tardigrades","Rotifers","Nematodes","Enchytraeids","Acari","Collembola","Protura","Diplura","Pseudoscorpions","Ants","Termites","Isopoda","Myriapoda","Earthworms","Coleoptera","Arachnida","Gastropoda","Larvae","Others")

iterations = length(whichTaxa)
variables = 1

output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichTaxa[i], ignore.case = TRUE, as.character(metadatreview$TaxaGroupAtlas)))
}

output <- data.frame(output)
output$Taxagroup <- whichTaxa
output$no.studies <- output$output
output$Taxagroup2 <- reorder(output$Taxagroup, output$output)
 

ggplot(output, aes(x = Taxagroup2, y = no.studies))+
  geom_col()+
   theme_bw() +
  theme(axis.text.y=element_text(face = "bold", size = rel(2)), 
        axis.text.x=element_text(angle = 45, hjust = 1, size = rel(2)),
        axis.title.y = element_text(size=18, face = "bold"),
        
        #legend
        legend.title = element_blank() ,
        legend.text = element_text(size=16), 
        legend.position = "bottom",
        
        #for the facets
        strip.text.x = element_text(size=16, face = "bold"),
        strip.background = element_rect(colour="black", fill="white"))


kable(output[,1:2])

output Taxagroup
7 Protists
4 Tardigrades
2 Rotifers
119 Nematodes
36 Enchytraeids
95 Acari
87 Collembola
13 Protura
17 Diplura
12 Pseudoscorpions
26 Ants
5 Termites
32 Isopoda
46 Myriapoda
81 Earthworms
55 Coleoptera
47 Arachnida
16 Gastropoda
36 Larvae
38 Others

NA

How many studies addressing macrofauna?

metadatreview$Macrofaune <- factor(ifelse(grepl("Ants|Termites|Isopoda|Myriapoda|Earthworms|Coleoptera|Arachnida|Gastropoda", 
                                  ignore.case = TRUE, as.character(metadatreview$TaxaGroupAtlas)), "Macrofauna", "Not macrofauna"))

summary(metadatreview$Macrofaune)
    Macrofauna Not macrofauna 
           106            168 

Figure 2C - alluvial diagram

Alluvial diagram showing that a few emblematic taxonomic groups and pollutant types dominate research

2. Ecosystem perspective

What is the scope of studies on chemical stressors impacts on natural soil fauna communities?

Figure 3- Scope of research

Studies addressing topics relevant to reach an ecosystem perspective

Studies addressing topics relevant to reach an ecosystem perspective

2.1. Multitrophic biodiversity and foodwebs

How many soil fauna taxonomic groups per study?

# count the no. of commas in the list
# subset dataframe keeping only paperID and drivers
df_taxo <- subset(metadatreview, select = c(X, TaxaGroupAtlas))

# split multiple taxa and pollutants into separate rows with strsplit
new_df_taxolist <- strsplit(as.character(df_taxo$TaxaGroupAtlas), ',')
new_df_combi_taxa <- data.frame(
  TaxaGroupAtlas = unlist(new_df_taxolist),
  PaperID = rep(df_taxo$X, sapply(new_df_taxolist, FUN = length)))

# no. of taxo group per paper
new_df_counts <- new_df_combi_taxa %>% group_by(PaperID) %>% summarise(no.taxa = n())

# descriptive stats
summary(new_df_counts)
    PaperID          no.taxa      
 Min.   :  1.00   Min.   : 1.000  
 1st Qu.: 69.25   1st Qu.: 1.000  
 Median :137.50   Median : 1.000  
 Mean   :137.50   Mean   : 2.836  
 3rd Qu.:205.75   3rd Qu.: 3.000  
 Max.   :274.00   Max.   :14.000  
hist(new_df_counts$no.taxa)

table(new_df_counts$no.taxa)

  1   2   3   4   5   6   7   8   9  10  11  12  13  14 
161  32  16   9   8  10   5   9   4   9   4   3   1   3 
# Calculate the percentage of each group
new_df_counts <- new_df_counts %>%
  group_by(no.taxa) %>%
  summarise(no.studies = n(), Percent = n()/nrow(.) * 100)

kable(new_df_counts)

no.taxa no.studies Percent
1 161 58.7591241
2 32 11.6788321
3 16 5.8394161
4 9 3.2846715
5 8 2.9197080
6 10 3.6496350
7 5 1.8248175
8 9 3.2846715
9 4 1.4598540
10 9 3.2846715
11 4 1.4598540
12 3 1.0948905
13 1 0.3649635
14 3 1.0948905

NA
NA

Foodweb mentioned in the abstract?

Search the abstracts of the studies for foodweb, food-web, trophic: 4 papers have foodweb in the abstract, only one paper has it in the title, 43 have trophic in the abstract (nematodes), 2 in the title.


# number of papers with those keywords in the abstract
whichKeywords <- c("foodweb|food-web")


iterations = length(whichKeywords)
variables = 1

output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichKeywords[i], ignore.case = TRUE, as.character(metadatreview$abstract)))
}

output <- data.frame(output)
output$Keyword <- whichKeywords
output

# number of papers with those keywords in the title
whichKeywords <- c("foodweb|food-web", "trophic")


iterations = length(whichKeywords)
variables = 1

output2 <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output2[i,] <- length(grep(whichKeywords[i], ignore.case = TRUE, as.character(metadatreview$title)))
}

output2 <- data.frame(output2)
output2$Keyword <- whichKeywords
output2
NA

What aspect of fauna diversity did the studies measured?

whichDiv <- c("Abundance","Biomass","Richness","Shannon","Evenness", "Others")

iterations = length(whichDiv)
variables = 1

output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichDiv[i], ignore.case = TRUE, as.character(metadatreview$DiversityMetric)))
}

output <- data.frame(output)
output$Measurement <- whichDiv

output

Multitrophic-diversity: how often other groups were studied ?

metadatreview <- metadatreview %>% 
  mutate(OtherBiodiversity = factor(ifelse(OtherBiodiversity=="lichens", "plants", # lichens in the plant category for simplification
                                           as.character(OtherBiodiversity))))

# Calculate the percentage of each group
metadatreviewsummary <- metadatreview %>%
  group_by(OtherBiodiversity) %>%
  summarise(no.studies = n(), Percent = n()/nrow(.) * 100)

kable(metadatreviewsummary)
OtherBiodiversity no.studies Percent
aboveground invertebrates 2 0.7299270
microbes 37 13.5036496
no 198 72.2627737
plants 20 7.2992701
plants,aboveground invertebrates 1 0.3649635
plants,microbes 16 5.8394161

2.2. Ecosystem functioning

Proportion of studies addressing one or several functions?

# Calculate the percentage of studies that have no,, one or several functions (based on full text screening)
metadatreviewsummary2 <- metadatreview %>%
  group_by(EcosystemFunction) %>%
  summarise(n(), Percent = n()/nrow(.) * 100)

kable(metadatreviewsummary2)
EcosystemFunction n() Percent
none 231 84.306569
one 27 9.854015
several 16 5.839416
# how many papers have function in the abstract?
subsetEF <- metadatreview[grepl("function", metadatreview$abstract),]
nrow(subsetEF)
[1] 59
# removing "functional groups" (nematodes studies often reported this)
nrow(subsetEF[!grepl("functional", subsetEF$abstract),])
[1] 23

2.3. Multiple global drivers

How often studies considered other drivers of global change?

# create the multiple stressors var
metadatreview <- metadatreview %>% 
  mutate(Multistressors = factor(ifelse(grepl(",", PollutantType), "Multiple stressors", 
                                        "Single stressor")),
         Multidrivers = factor(ifelse(LandUse=="TRUE"|Intensification=="TRUE"|Fragmentation.Loss=="TRUE"|Nutrient=="TRUE"|ClimateChange=="TRUE"|Invasives=="TRUE", "yes", "no")))

# Calculate the percentage of each group
metadatreviewsummary3 <- metadatreview %>%
  group_by(Multidrivers) %>%
  summarise(no.studies = n(), Percent = n()/nrow(.) * 100)

Drivers considered


# create the list of vector each corresponding to a separate circle
multiGCDpapers <- data.frame(no.stu = t(
  data.frame(
    withLUI = nrow(metadatreview[metadatreview$Intensification == "TRUE", ]),
    withLUC = nrow(metadatreview[metadatreview$LandUse == "TRUE", ]),
    withInvas = nrow(metadatreview[metadatreview$Invasives == "TRUE", ]),
    withClim = nrow(metadatreview[metadatreview$ClimateChange == "TRUE", ]),
    withNut = nrow(metadatreview[metadatreview$Nutrient == "TRUE", ]),
    withFragm = nrow(metadatreview[metadatreview$Fragmentation.Loss == "TRUE", ])
  )
))

multiGCDpapers$OtherDrivers <- rownames(multiGCDpapers)

kable(multiGCDpapers)

no.stu OtherDrivers
withLUI 28 withLUI
withLUC 22 withLUC
withInvas 1 withInvas
withClim 5 withClim
withNut 37 withNut
withFragm 0 withFragm

NA
NA

Study types - experimental or observational?

summary(metadatreview$ExperimentObservation)
                                         Experimental 
                         1                        133 
Experimental,Observational              Observational 
                         1                        139 

What types of ecosystems/land covers are represented?

whichSystem <- c("Grassland","Woodland","Cropland","Wetland","Artifical","Bare land","Shrubland","Others")

iterations = length(whichSystem)
variables = 1

output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichSystem[i], ignore.case = TRUE, as.character(metadatreview$System)))
}

output <- data.frame(output)
output$System <- whichSystem
output$no.studies <- output$output
output$System2 <- reorder(output$System, output$output)
 


ggplot(output, aes(x = System2, y = no.studies))+
  geom_col()+
   theme_bw() +
  theme(axis.text.y=element_text(face = "bold", size = rel(2)), 
        axis.text.x=element_text(angle = 45, hjust = 1, size = rel(2)),
        axis.title.y = element_text(size=18, face = "bold"),
        
        #legend
        legend.title = element_blank() ,
        legend.text = element_text(size=16), 
        legend.position = "bottom",
        
        #for the facets
        strip.text.x = element_text(size=16, face = "bold"),
        strip.background = element_rect(colour="black", fill="white"))

NA
NA

Citations of papers that addressed bioindicators


# # 22 studies have bioindicator in the abstract
whichKeywords <- c("bioindicator|bio-indicator")


iterations = length(whichKeywords)
variables = 1

output <- matrix(ncol=variables, nrow=iterations)

for(i in 1:iterations){
  output[i,] <- length(grep(whichKeywords[i], ignore.case = TRUE, as.character(metadatreview$abstract)))
}

output <- data.frame(output)
output$Keyword <- whichKeywords
output

# list those papers
BioIndicStudiesAbstract <- metadatreview[grepl("bioindicator", metadatreview$abstract),]
BioIndicStudiesTitle <- metadatreview[grepl("bioindicator", metadatreview$title),]
BioIndicStudiesTitle

Supplement Tables

Supplement Table: all papers with multitrophic-diversity

The list of papers focusing on plants, microbes and soil fauna was used to investigate whether the studies addressed indirect effects of chemical stressors, and if they considered foodweb or network approaches.

# create a table with all studies that have at least one EF
WhichVars <- c("authors", "Published.Year", "journal", "title", "DOI", "Country", "DiversityMetric", "PollutantName", "PollutantType", "PollutionSource", "TaxaGroupAtlas", "System", "OtherBiodiversity", "EcosystemFunction", "Multidrivers")

MBdata <- metadatreview[metadatreview$OtherBiodiversity != "no",WhichVars]


write.csv(MBdata, "Output/TablePapersWithMultiDiv.csv")

Supplement Table: all papers with functions

The list of papers having at least one function was used to investigate what type of functions and ecosystem services were addressed in those studies.

# create a table with all studies that have at least one EF
WhichVars <- c("authors", "Published.Year", "journal", "title", "DOI", "Country", "DiversityMetric", "PollutantName", "PollutantType", "PollutionSource", "TaxaGroupAtlas", "System", "OtherBiodiversity", "EcosystemFunction", "Multidrivers")

EFdata <- metadatreview[metadatreview$EcosystemFunction != "none",WhichVars]


write.csv(EFdata, "Output/TablePapersWithEF.csv")

Supplement Table: all papers with multi-drivers

The list of papers having multiple global change drivers was used to investigate how often studies had a full factorial design enabling to address interactive effects.

WhichVars <- c("authors", "Published.Year", "journal", "title", "DOI", "Country", "DiversityMetric", "PollutantName", "PollutantType", "PollutionSource", "TaxaGroupAtlas", "System", "OtherBiodiversity", "EcosystemFunction", "Multidrivers",
               "ClimateChange", "Nutrient", "LandUse",
               "Intensification", "Fragmentation.Loss", "Invasives")

MultiGCDdata <- metadatreview[metadatreview$Multidrivers != "no",WhichVars]


write.csv(MultiGCDdata, "Output/TablePapersWithMultiGCDs.csv")
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKUmVzdWx0cyBwcmVzZW50ZWQgaW4gIlNvaWwgYmlvZGl2ZXJzaXR5IGFuZCBjaGVtaWNhbCBzdHJlc3NvcnM6IGdsb2JhbCByZXZpZXcgYW5kIHJvYWRtYXAgZm9yIGZ1dHVyZSByZXNlYXJjaCIuIAoKRmlndXJlcyBhcmUgY3JlYXRlZCBmcm9tIHRoZSBzY3JpcHQgIkZJR1VSRVMuUiIuCgojIyBEYXRhCmBgYHtyIGxvYWRkYXR9CmxpYnJhcnkoZHBseXIpCiMgbG9hZCBwYWNrYWdlcyBmb3IgbWFwcwpsaWJyYXJ5KG1hcHRvb2xzKQpsaWJyYXJ5KGdnbWFwKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpCmxpYnJhcnkoY291bnRyeWNvZGUpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShyZXNoYXBlMikKCmAlbm90aW4lYCA9IE5lZ2F0ZShgJWluJWApCgojIERBVEEtLS0tLS0tLQptZXRhZGF0cmV2aWV3IDwtIHJlYWQuY3N2KCJEYXRhL1JlZmVyZW5jZXNfcG9sbHV0aW9uX3Jldmlldy5jc3YiKQoKYGBgCgoKIyMgSG93IG1hbnkgc3R1ZGllcyBpbmNsdWRlZD8KYGBge3J9Cm5yb3cobWV0YWRhdHJldmlldykKYGBgCgojIDEuIE1hcHBpbmcga25vd2xlZGdlIGdhcHMKCiMjIDEuMS4gR2VvZ3JhcGhpY2FsIGJpYXMKCkFyZSB0aGVyZSBnZW9ncmFwaGljYWwgYmlhcyBhbmQgd2hhdCByZWdpb25zIGFyZSBwb29ybHkgY292ZXJlZCA/CgohW0Rpc3RyaWJ1dGlvbiBvZiBzdHVkaWVzIGluY2x1ZGVkIGJ5IGNvdW50cnldKGZpZ3MvRmlnMkFfbWFwLnBuZykKCkNvdW50cmllcyBtb3N0IHJlcHJlc2VudGVkPyAKYGBge3J9CnN0dWQgPC0gbWV0YWRhdHJldmlldyAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lIAogIHN1bW1hcml6ZShuby5zdHUgPSBuKCkpCgptdWx0aWNvdW50cnlzdHVkIDwtIHN0dWRbZ3JlcGwoIjt8LCIsIHN0dWQkQ291bnRyeSksXQoKIyBNYW51YWxseSBhZGQgbXVsdGlwbGUgY291bnRyaWVzIHN0dWRpZXM6CmFkZGluZ2NvdW50cmllcyA8LSBkYXRhLmZyYW1lKHJiaW5kKGMoIkJyYXppbCIsIDEpLCBjKCJQb3J0dWdhbCIsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiQ2FuYWRhIiwgMSksIGMoImZyYW5jZSIsIDEpLCBjKCJUaGUgTmV0aGVybGFuZHMiLCAxKSwgYygiU3dpdHplcmxhbmQiLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiR2VybWFueSIsIDEpLCBjKCJUaGUgTmV0aGVybGFuZHMiLCAxKSwgYygiVUsiLCAxKSwgYygiUG9ydHVnYWwiLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kb25lc2lhIiwgMSksYygiTWFsYXlzaWEiLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVUsiLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVGhlIE5ldGhlcmxhbmRzIiwgMSksIGMoIkJlbGdpdW0iLCAxKSwgYygiZnJhbmNlIiwgMSksIGMoImdlcm1hbnkiLCAxKSkpCmNvbG5hbWVzKGFkZGluZ2NvdW50cmllcykgPC0gY29sbmFtZXMoc3R1ZCkKYWRkaW5nY291bnRyaWVzJG5vLnN0dSA8LSBhcy5udW1lcmljKGFkZGluZ2NvdW50cmllcyRuby5zdHUpCgpzdHVkIDwtIHJiaW5kKGFzLmRhdGEuZnJhbWUoc3R1ZCksIGFkZGluZ2NvdW50cmllcykKCiMgaGFybW9uaXplIGNvdW50cnkgbmFtZXMKc3R1ZCA8LSBzdHVkICU+JQogIG11dGF0ZShpc28gPSAgY291bnRyeWNvZGUoc3R1ZCRDb3VudHJ5LCBvcmlnaW4gPSAiY291bnRyeS5uYW1lIiwgZGVzdGluYXRpb24gPSAiaXNvM2MiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGlzbykgJT4lCiAgc3VtbWFyaXplKG5vLnN0dWQgPSBzdW0obm8uc3R1LCBuYS5ybSA9IFRSVUUpKQoKc3R1ZFtzdHVkJG5vLnN0dWQgPiAyMCxdCmBgYAoKCiMjIDEuMi4gUG9sbHV0YW50IHR5cGVzCgojIyMgVGltZSBzcGFuIG9mIHN0dWRpZXM/IApgYGB7cn0Kc3VtbWFyeShtZXRhZGF0cmV2aWV3JFB1Ymxpc2hlZC5ZZWFyKQpgYGAKCgojIyMgUG9sbHV0YW50cyBtb3N0L2xlYXN0IHJlcHJlc2VudGVkPyAKYGBge3J9CndoaWNoUG9sbHV0aW9uVHlwZSA8LSBjKCJNZXRhbHMiLCAiUEFIIiwgIlBlc3RpY2lkZXMiLCAiUGxhc3RpY3MvUGxhc3RpY2l6ZXJzIiwiUGhhcm1hY2V1dGljYWxzIiwiUmFkaWF0aW9uIiwgIk5hbm9wYXJ0aWNsZXMiLCAiT3RoZXJzIikKCml0ZXJhdGlvbnMgIDwtICBsZW5ndGgod2hpY2hQb2xsdXRpb25UeXBlKQp2YXJpYWJsZXMgPC0gIDEKCgpvdXRwdXQgPC0gbWF0cml4KG5jb2w9dmFyaWFibGVzLCBucm93PWl0ZXJhdGlvbnMpCgpmb3IoaSBpbiAxOml0ZXJhdGlvbnMpewogIG91dHB1dFtpLF0gPC0gbGVuZ3RoKGdyZXAod2hpY2hQb2xsdXRpb25UeXBlW2ldLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JFBvbGx1dGFudFR5cGUpKSkKfQoKb3V0cHV0IDwtIGRhdGEuZnJhbWUoUG9sbHV0aW9uVHlwZSA9IHdoaWNoUG9sbHV0aW9uVHlwZSwKICAgICAgICAgICAgICAgICAgICAgbm8uc3R1ZGllcyA9IG91dHB1dCkKCmthYmxlKG91dHB1dCkKCmBgYAoKIyMjIEZpZ3VyZSAyQiAtIFRpbWUgdHJlbmRzIAohW0N1bXVsYXRpdmUgbnVtYmVyIG9mIHN0dWRpZXMgb24gZWFjaCB0eXBlIG9mIHBvbGx1dGFudCBzdHVkaWVkIHRocm91Z2ggdGltZV0oZmlncy9GaWcyQl9Qb2xsdXRhbnRzLnBuZykKCgojIyMgV2hhdCBhcmUgdGhlIGh1bWFuIGFjdGl2aXRpZXMgcmVzcG9uc2libGUgZm9yIHNvaWwgcG9sbHV0aW9uIHRoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbmx5IHN0dWRpZWQgPyAKCmBgYHtyfQp3aGljaFBvbGx1dGlvblNvdXJjZSA8LSBjKCJBZ3JpY3VsdHVyYWwvbGl2ZXN0b2NrIiwiSW5kdXN0cmlhbCIsIk1pbmluZy9TbWVsdGluZyIsIk1pbGl0YXJ5L1dhcnMiLCAgICAgICAgICAgICAgICAgICAgICAgICJOYXR1cmFsL2dlb2dlbmljIiwiV2FzdGUvU2V3YWdlIiwiVXJiYW4vVHJhbnNwb3J0IiwiT3RoZXJzIikKCml0ZXJhdGlvbnMgIDwtICBsZW5ndGgod2hpY2hQb2xsdXRpb25Tb3VyY2UpCnZhcmlhYmxlcyA8LSAgMQoKCm91dHB1dCA8LSBtYXRyaXgobmNvbD12YXJpYWJsZXMsIG5yb3c9aXRlcmF0aW9ucykKCmZvcihpIGluIDE6aXRlcmF0aW9ucyl7CiAgIG91dHB1dFtpLF0gPC0gbGVuZ3RoKGdyZXAod2hpY2hQb2xsdXRpb25Tb3VyY2VbaV0sIGlnbm9yZS5jYXNlID0gVFJVRSwgYXMuY2hhcmFjdGVyKG1ldGFkYXRyZXZpZXckUG9sbHV0aW9uU291cmNlKSkpCn0KCm91dHB1dCA8LSBkYXRhLmZyYW1lKG91dHB1dCkKb3V0cHV0JFBvbGx1dGlvblNvdXJjZSA8LSB3aGljaFBvbGx1dGlvblNvdXJjZQoKa2FibGUob3V0cHV0KQoKYGBgCgoKYGBge3J9CiMgc3VtIHVwIGluZHVzdHJpYWwvbWluaW5nL3dhc3RlIChvdmVybGFwcykKc3VtKG91dHB1dCRvdXRwdXRbb3V0cHV0JFBvbGx1dGlvblNvdXJjZT09IkluZHVzdHJpYWwiXSwKICAgIG91dHB1dCRvdXRwdXRbb3V0cHV0JFBvbGx1dGlvblNvdXJjZT09Ik1pbmluZy9TbWVsdGluZyJdKQpgYGAKCgpXaGF0IGFyZSB0aGUgIm90aGVycyIgaW4gUG9sbHV0YW50IHR5cGUgY2F0ZWdvcnk6IAotIHNhbHRzCi0gZXhwbG9zaXZlcwoKYGBge3J9Cm1ldGFkYXRyZXZpZXckUG9sbHV0YW50TmFtZVttZXRhZGF0cmV2aWV3JFBvbGx1dGFudFR5cGU9PSJPdGhlcnMiXQpgYGAKCiMjIyBNaXh0dXJlIG9mIGNoZW1pY2Fsczogd2hhdCBzY2VuYXJpb3Mgc3R1ZGllZD8KYGBge3J9CiMgY3JlYXRlIG5ldyBwb2wgdHlwZSB3aGVyZSA9IG1peHR1cmUgd2hlbiBtb3JlIHRoYW4gb25lIGNhdGVnb3J5IG9mIHBvbGx1dGFudHMgYXJlIGludm9sdmVkCm1ldGFkYXRyZXZpZXckUG9sbHV0YW50VHlwZTIgPC0gYXMuZmFjdG9yKGlmZWxzZShncmVwbCgiLCIsIG1ldGFkYXRyZXZpZXckUG9sbHV0YW50VHlwZSksICJNaXh0dXJlIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIobWV0YWRhdHJldmlldyRQb2xsdXRhbnRUeXBlKSkpCgpzdW1tYXJ5KG1ldGFkYXRyZXZpZXckUG9sbHV0YW50VHlwZTIpCmBgYAoKCldoZW4gYWNjb3VudGluZyBmb3IgbWl4dHVyZSBhY3Jvc3MgQU5EIHdpdGhpbiBwb2xsdXRhbnQgY2F0ZWdvcmllczogbW9zdCBzdHVkaWVzIGZvY3VzIG9uIG11bHRpcGxlIGNoZW1pY2Fscy4gSW5kZWVkLCBoYWxmIG9mIHRoZSBzdHVkaWVzIGFyZSBvYnNlcnZhdGlvbmFsLCBhbmQgbW9zdCBjb250YW1pbmF0aW9ucyBhbmQgcG9sbHV0aW9ucyBpbnZvbHZlIG11bHRpcGxlIGNoZW1pY2FscyBpbiByZWFsLXdvcmxkIHNjZW5hcmlvcy4KYGBge3J9CiMgY3JlYXRlIG5ldyBwb2wgdHlwZSB3aGVyZSA9IG1peHR1cmUgd2hlbiBtb3JlIHRoYW4gb25lIGNhdGVnb3J5IG9mIHBvbGx1dGFudCBpcyBpbnZvbHZlZCAoZm9yIHBsb3QgcHVycG9zZXMpCm1ldGFkYXRyZXZpZXckUG9sbHV0YW50VHlwZTMgPC1hcy5mYWN0b3IoaWZlbHNlKAogICMgbWl4dHVyZXMgYWNyb3NzIGFuZCB3aXRoaW4gY2F0ZWdvcmVpcyBvZiBzdHJlc3NvcnMKICBncmVwbCgiLHw7fGFuZHxzZXZlcmFsfG1hbnl8bXVsdGlwbGV8bmFub21hdGVyaWFsc3xtZXRhbHN8Y2lkZXN8Y2FyYm9uc3xQQUhzfGFnZW50c3xyZXNpZHVlcyIsIG1ldGFkYXRyZXZpZXckUG9sbHV0YW50TmFtZSl8CiAgICBncmVwbCgiLCIsIG1ldGFkYXRyZXZpZXckUG9sbHV0YW50VHlwZSksICJNaXh0dXJlcyIsIAogIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JFBvbGx1dGFudFR5cGUpKSkKCnN1bW1hcnkobWV0YWRhdHJldmlldyRQb2xsdXRhbnRUeXBlMykKCmBgYAoKCgojIyAxLjMuIFRheG9ub21pYyBncm91cHMKCiMjIyBXaGF0IGFyZSB0aGUgdGF4YSBtb3N0IHJlcHJlc2VudGVkIGJ5IHRoZSBzdHVkaWVzPyAKCmBgYHtyfQp3aGljaFRheGEgPC0gYygiUHJvdGlzdHMiLCJUYXJkaWdyYWRlcyIsIlJvdGlmZXJzIiwiTmVtYXRvZGVzIiwiRW5jaHl0cmFlaWRzIiwiQWNhcmkiLCJDb2xsZW1ib2xhIiwiUHJvdHVyYSIsIkRpcGx1cmEiLCJQc2V1ZG9zY29ycGlvbnMiLCJBbnRzIiwiVGVybWl0ZXMiLCJJc29wb2RhIiwiTXlyaWFwb2RhIiwiRWFydGh3b3JtcyIsIkNvbGVvcHRlcmEiLCJBcmFjaG5pZGEiLCJHYXN0cm9wb2RhIiwiTGFydmFlIiwiT3RoZXJzIikKCml0ZXJhdGlvbnMgPSBsZW5ndGgod2hpY2hUYXhhKQp2YXJpYWJsZXMgPSAxCgpvdXRwdXQgPC0gbWF0cml4KG5jb2w9dmFyaWFibGVzLCBucm93PWl0ZXJhdGlvbnMpCgpmb3IoaSBpbiAxOml0ZXJhdGlvbnMpewogIG91dHB1dFtpLF0gPC0gbGVuZ3RoKGdyZXAod2hpY2hUYXhhW2ldLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JFRheGFHcm91cEF0bGFzKSkpCn0KCm91dHB1dCA8LSBkYXRhLmZyYW1lKG91dHB1dCkKb3V0cHV0JFRheGFncm91cCA8LSB3aGljaFRheGEKb3V0cHV0JG5vLnN0dWRpZXMgPC0gb3V0cHV0JG91dHB1dApvdXRwdXQkVGF4YWdyb3VwMiA8LSByZW9yZGVyKG91dHB1dCRUYXhhZ3JvdXAsIG91dHB1dCRvdXRwdXQpCiAKCmdncGxvdChvdXRwdXQsIGFlcyh4ID0gVGF4YWdyb3VwMiwgeSA9IG5vLnN0dWRpZXMpKSsKICBnZW9tX2NvbCgpKwogICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IHJlbCgyKSksIAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSByZWwoMikpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAKICAgICAgICAjbGVnZW5kCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpICwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwgCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgCiAgICAgICAgI2ZvciB0aGUgZmFjZXRzCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIikpCgprYWJsZShvdXRwdXRbLDE6Ml0pCgpgYGAKCiMjIyBIb3cgbWFueSBzdHVkaWVzIGFkZHJlc3NpbmcgbWFjcm9mYXVuYT8KCmBgYHtyfQptZXRhZGF0cmV2aWV3JE1hY3JvZmF1bmUgPC0gZmFjdG9yKGlmZWxzZShncmVwbCgiQW50c3xUZXJtaXRlc3xJc29wb2RhfE15cmlhcG9kYXxFYXJ0aHdvcm1zfENvbGVvcHRlcmF8QXJhY2huaWRhfEdhc3Ryb3BvZGEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVFJVRSwgYXMuY2hhcmFjdGVyKG1ldGFkYXRyZXZpZXckVGF4YUdyb3VwQXRsYXMpKSwgIk1hY3JvZmF1bmEiLCAiTm90IG1hY3JvZmF1bmEiKSkKCnN1bW1hcnkobWV0YWRhdHJldmlldyRNYWNyb2ZhdW5lKQpgYGAKCiMjIyBGaWd1cmUgMkMgLSBhbGx1dmlhbCBkaWFncmFtCiFbQWxsdXZpYWwgZGlhZ3JhbSBzaG93aW5nIHRoYXQgYSBmZXcgZW1ibGVtYXRpYyB0YXhvbm9taWMgZ3JvdXBzIGFuZCBwb2xsdXRhbnQgdHlwZXMgZG9taW5hdGUgcmVzZWFyY2hdKGZpZ3MvRGF0YWdhcHNfZ3JleS5wZGYpCgoKIyAyLiBFY29zeXN0ZW0gcGVyc3BlY3RpdmUKCldoYXQgaXMgdGhlIHNjb3BlIG9mIHN0dWRpZXMgb24gY2hlbWljYWwgc3RyZXNzb3JzIGltcGFjdHMgb24gbmF0dXJhbCBzb2lsIGZhdW5hIGNvbW11bml0aWVzPwoKIyMgRmlndXJlIDMtIFNjb3BlIG9mIHJlc2VhcmNoCiFbU3R1ZGllcyBhZGRyZXNzaW5nIHRvcGljcyByZWxldmFudCB0byByZWFjaCBhbiBlY29zeXN0ZW0gcGVyc3BlY3RpdmVdKGZpZ3MvRmlnM19TY29wZS5wbmcpCgoKIyMgMi4xLiBNdWx0aXRyb3BoaWMgYmlvZGl2ZXJzaXR5IGFuZCBmb29kd2VicwoKIyMjIEhvdyBtYW55IHNvaWwgZmF1bmEgdGF4b25vbWljIGdyb3VwcyBwZXIgc3R1ZHk/CmBgYHtyfQojIGNvdW50IHRoZSBuby4gb2YgY29tbWFzIGluIHRoZSBsaXN0CiMgc3Vic2V0IGRhdGFmcmFtZSBrZWVwaW5nIG9ubHkgcGFwZXJJRCBhbmQgZHJpdmVycwpkZl90YXhvIDwtIHN1YnNldChtZXRhZGF0cmV2aWV3LCBzZWxlY3QgPSBjKFgsIFRheGFHcm91cEF0bGFzKSkKCiMgc3BsaXQgbXVsdGlwbGUgdGF4YSBhbmQgcG9sbHV0YW50cyBpbnRvIHNlcGFyYXRlIHJvd3Mgd2l0aCBzdHJzcGxpdApuZXdfZGZfdGF4b2xpc3QgPC0gc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGRmX3RheG8kVGF4YUdyb3VwQXRsYXMpLCAnLCcpCm5ld19kZl9jb21iaV90YXhhIDwtIGRhdGEuZnJhbWUoCiAgVGF4YUdyb3VwQXRsYXMgPSB1bmxpc3QobmV3X2RmX3RheG9saXN0KSwKICBQYXBlcklEID0gcmVwKGRmX3RheG8kWCwgc2FwcGx5KG5ld19kZl90YXhvbGlzdCwgRlVOID0gbGVuZ3RoKSkpCgojIG5vLiBvZiB0YXhvIGdyb3VwIHBlciBwYXBlcgpuZXdfZGZfY291bnRzIDwtIG5ld19kZl9jb21iaV90YXhhICU+JSBncm91cF9ieShQYXBlcklEKSAlPiUgc3VtbWFyaXNlKG5vLnRheGEgPSBuKCkpCgojIGRlc2NyaXB0aXZlIHN0YXRzCnN1bW1hcnkobmV3X2RmX2NvdW50cykKaGlzdChuZXdfZGZfY291bnRzJG5vLnRheGEpCnRhYmxlKG5ld19kZl9jb3VudHMkbm8udGF4YSkKCiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGVhY2ggZ3JvdXAKbmV3X2RmX2NvdW50cyA8LSBuZXdfZGZfY291bnRzICU+JQogIGdyb3VwX2J5KG5vLnRheGEpICU+JQogIHN1bW1hcmlzZShuby5zdHVkaWVzID0gbigpLCBQZXJjZW50ID0gbigpL25yb3coLikgKiAxMDApCgprYWJsZShuZXdfZGZfY291bnRzKQoKCmBgYAoKIyMjIEZvb2R3ZWIgbWVudGlvbmVkIGluIHRoZSBhYnN0cmFjdD8gCgpTZWFyY2ggdGhlIGFic3RyYWN0cyBvZiB0aGUgc3R1ZGllcyBmb3IgZm9vZHdlYiwgZm9vZC13ZWIsIHRyb3BoaWM6IDQgcGFwZXJzIGhhdmUgZm9vZHdlYiBpbiB0aGUgYWJzdHJhY3QsIG9ubHkgb25lIHBhcGVyIGhhcyBpdCBpbiB0aGUgdGl0bGUsIDQzIGhhdmUgdHJvcGhpYyBpbiB0aGUgYWJzdHJhY3QgKG5lbWF0b2RlcyksIDIgaW4gdGhlIHRpdGxlLgoKYGBge3J9CgojIG51bWJlciBvZiBwYXBlcnMgd2l0aCB0aG9zZSBrZXl3b3JkcyBpbiB0aGUgYWJzdHJhY3QKd2hpY2hLZXl3b3JkcyA8LSBjKCJmb29kd2VifGZvb2Qtd2ViIikKCgppdGVyYXRpb25zID0gbGVuZ3RoKHdoaWNoS2V5d29yZHMpCnZhcmlhYmxlcyA9IDEKCm91dHB1dCA8LSBtYXRyaXgobmNvbD12YXJpYWJsZXMsIG5yb3c9aXRlcmF0aW9ucykKCmZvcihpIGluIDE6aXRlcmF0aW9ucyl7CiAgb3V0cHV0W2ksXSA8LSBsZW5ndGgoZ3JlcCh3aGljaEtleXdvcmRzW2ldLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JGFic3RyYWN0KSkpCn0KCm91dHB1dCA8LSBkYXRhLmZyYW1lKG91dHB1dCkKb3V0cHV0JEtleXdvcmQgPC0gd2hpY2hLZXl3b3JkcwpvdXRwdXQKCiMgbnVtYmVyIG9mIHBhcGVycyB3aXRoIHRob3NlIGtleXdvcmRzIGluIHRoZSB0aXRsZQp3aGljaEtleXdvcmRzIDwtIGMoImZvb2R3ZWJ8Zm9vZC13ZWIiLCAidHJvcGhpYyIpCgoKaXRlcmF0aW9ucyA9IGxlbmd0aCh3aGljaEtleXdvcmRzKQp2YXJpYWJsZXMgPSAxCgpvdXRwdXQyIDwtIG1hdHJpeChuY29sPXZhcmlhYmxlcywgbnJvdz1pdGVyYXRpb25zKQoKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICBvdXRwdXQyW2ksXSA8LSBsZW5ndGgoZ3JlcCh3aGljaEtleXdvcmRzW2ldLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JHRpdGxlKSkpCn0KCm91dHB1dDIgPC0gZGF0YS5mcmFtZShvdXRwdXQyKQpvdXRwdXQyJEtleXdvcmQgPC0gd2hpY2hLZXl3b3JkcwpvdXRwdXQyCgpgYGAKCgoKIyMjIFdoYXQgYXNwZWN0IG9mIGZhdW5hIGRpdmVyc2l0eSBkaWQgdGhlIHN0dWRpZXMgbWVhc3VyZWQ/CmBgYHtyfQp3aGljaERpdiA8LSBjKCJBYnVuZGFuY2UiLCJCaW9tYXNzIiwiUmljaG5lc3MiLCJTaGFubm9uIiwiRXZlbm5lc3MiLCAiT3RoZXJzIikKCml0ZXJhdGlvbnMgPSBsZW5ndGgod2hpY2hEaXYpCnZhcmlhYmxlcyA9IDEKCm91dHB1dCA8LSBtYXRyaXgobmNvbD12YXJpYWJsZXMsIG5yb3c9aXRlcmF0aW9ucykKCmZvcihpIGluIDE6aXRlcmF0aW9ucyl7CiAgb3V0cHV0W2ksXSA8LSBsZW5ndGgoZ3JlcCh3aGljaERpdltpXSwgaWdub3JlLmNhc2UgPSBUUlVFLCBhcy5jaGFyYWN0ZXIobWV0YWRhdHJldmlldyREaXZlcnNpdHlNZXRyaWMpKSkKfQoKb3V0cHV0IDwtIGRhdGEuZnJhbWUob3V0cHV0KQpvdXRwdXQkTWVhc3VyZW1lbnQgPC0gd2hpY2hEaXYKCm91dHB1dApgYGAKCgojIyMgTXVsdGl0cm9waGljLWRpdmVyc2l0eTogaG93IG9mdGVuIG90aGVyIGdyb3VwcyB3ZXJlIHN0dWRpZWQgPwpgYGB7cn0KbWV0YWRhdHJldmlldyA8LSBtZXRhZGF0cmV2aWV3ICU+JSAKICBtdXRhdGUoT3RoZXJCaW9kaXZlcnNpdHkgPSBmYWN0b3IoaWZlbHNlKE90aGVyQmlvZGl2ZXJzaXR5PT0ibGljaGVucyIsICJwbGFudHMiLCAjIGxpY2hlbnMgaW4gdGhlIHBsYW50IGNhdGVnb3J5IGZvciBzaW1wbGlmaWNhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKE90aGVyQmlvZGl2ZXJzaXR5KSkpKQoKIyBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgZWFjaCBncm91cAptZXRhZGF0cmV2aWV3c3VtbWFyeSA8LSBtZXRhZGF0cmV2aWV3ICU+JQogIGdyb3VwX2J5KE90aGVyQmlvZGl2ZXJzaXR5KSAlPiUKICBzdW1tYXJpc2Uobm8uc3R1ZGllcyA9IG4oKSwgUGVyY2VudCA9IG4oKS9ucm93KC4pICogMTAwKQoKa2FibGUobWV0YWRhdHJldmlld3N1bW1hcnkpCmBgYAoKCgojIyAyLjIuIEVjb3N5c3RlbSBmdW5jdGlvbmluZwoKIyMjIFByb3BvcnRpb24gb2Ygc3R1ZGllcyBhZGRyZXNzaW5nIG9uZSBvciBzZXZlcmFsIGZ1bmN0aW9ucz8KYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHN0dWRpZXMgdGhhdCBoYXZlIG5vLCwgb25lIG9yIHNldmVyYWwgZnVuY3Rpb25zIChiYXNlZCBvbiBmdWxsIHRleHQgc2NyZWVuaW5nKQptZXRhZGF0cmV2aWV3c3VtbWFyeTIgPC0gbWV0YWRhdHJldmlldyAlPiUKICBncm91cF9ieShFY29zeXN0ZW1GdW5jdGlvbikgJT4lCiAgc3VtbWFyaXNlKG4oKSwgUGVyY2VudCA9IG4oKS9ucm93KC4pICogMTAwKQoKa2FibGUobWV0YWRhdHJldmlld3N1bW1hcnkyKQpgYGAKCgpgYGB7cn0KIyBob3cgbWFueSBwYXBlcnMgaGF2ZSBmdW5jdGlvbiBpbiB0aGUgYWJzdHJhY3Q/CnN1YnNldEVGIDwtIG1ldGFkYXRyZXZpZXdbZ3JlcGwoImZ1bmN0aW9uIiwgbWV0YWRhdHJldmlldyRhYnN0cmFjdCksXQpucm93KHN1YnNldEVGKQoKIyByZW1vdmluZyAiZnVuY3Rpb25hbCBncm91cHMiIChuZW1hdG9kZXMgc3R1ZGllcyBvZnRlbiByZXBvcnRlZCB0aGlzKQpucm93KHN1YnNldEVGWyFncmVwbCgiZnVuY3Rpb25hbCIsIHN1YnNldEVGJGFic3RyYWN0KSxdKQpgYGAKCgoKIyMgMi4zLiBNdWx0aXBsZSBnbG9iYWwgZHJpdmVycwoKIyMjIEhvdyBvZnRlbiBzdHVkaWVzIGNvbnNpZGVyZWQgb3RoZXIgZHJpdmVycyBvZiBnbG9iYWwgY2hhbmdlPwpgYGB7cn0KIyBjcmVhdGUgdGhlIG11bHRpcGxlIHN0cmVzc29ycyB2YXIKbWV0YWRhdHJldmlldyA8LSBtZXRhZGF0cmV2aWV3ICU+JSAKICBtdXRhdGUoTXVsdGlzdHJlc3NvcnMgPSBmYWN0b3IoaWZlbHNlKGdyZXBsKCIsIiwgUG9sbHV0YW50VHlwZSksICJNdWx0aXBsZSBzdHJlc3NvcnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaW5nbGUgc3RyZXNzb3IiKSksCiAgICAgICAgIE11bHRpZHJpdmVycyA9IGZhY3RvcihpZmVsc2UoTGFuZFVzZT09IlRSVUUifEludGVuc2lmaWNhdGlvbj09IlRSVUUifEZyYWdtZW50YXRpb24uTG9zcz09IlRSVUUifE51dHJpZW50PT0iVFJVRSJ8Q2xpbWF0ZUNoYW5nZT09IlRSVUUifEludmFzaXZlcz09IlRSVUUiLCAieWVzIiwgIm5vIikpKQoKIyBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgZWFjaCBncm91cAptZXRhZGF0cmV2aWV3c3VtbWFyeTMgPC0gbWV0YWRhdHJldmlldyAlPiUKICBncm91cF9ieShNdWx0aWRyaXZlcnMpICU+JQogIHN1bW1hcmlzZShuby5zdHVkaWVzID0gbigpLCBQZXJjZW50ID0gbigpL25yb3coLikgKiAxMDApCgpgYGAKCgoKIyMjIERyaXZlcnMgY29uc2lkZXJlZApgYGB7cn0KCiMgY3JlYXRlIHRoZSBsaXN0IG9mIHZlY3RvciBlYWNoIGNvcnJlc3BvbmRpbmcgdG8gYSBzZXBhcmF0ZSBjaXJjbGUKbXVsdGlHQ0RwYXBlcnMgPC0gZGF0YS5mcmFtZShuby5zdHUgPSB0KAogIGRhdGEuZnJhbWUoCiAgICB3aXRoTFVJID0gbnJvdyhtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckSW50ZW5zaWZpY2F0aW9uID09ICJUUlVFIiwgXSksCiAgICB3aXRoTFVDID0gbnJvdyhtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckTGFuZFVzZSA9PSAiVFJVRSIsIF0pLAogICAgd2l0aEludmFzID0gbnJvdyhtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckSW52YXNpdmVzID09ICJUUlVFIiwgXSksCiAgICB3aXRoQ2xpbSA9IG5yb3cobWV0YWRhdHJldmlld1ttZXRhZGF0cmV2aWV3JENsaW1hdGVDaGFuZ2UgPT0gIlRSVUUiLCBdKSwKICAgIHdpdGhOdXQgPSBucm93KG1ldGFkYXRyZXZpZXdbbWV0YWRhdHJldmlldyROdXRyaWVudCA9PSAiVFJVRSIsIF0pLAogICAgd2l0aEZyYWdtID0gbnJvdyhtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckRnJhZ21lbnRhdGlvbi5Mb3NzID09ICJUUlVFIiwgXSkKICApCikpCgptdWx0aUdDRHBhcGVycyRPdGhlckRyaXZlcnMgPC0gcm93bmFtZXMobXVsdGlHQ0RwYXBlcnMpCgprYWJsZShtdWx0aUdDRHBhcGVycykKCgpgYGAKCgoKCiMjIFN0dWR5IHR5cGVzIC0gZXhwZXJpbWVudGFsIG9yIG9ic2VydmF0aW9uYWw/CgpgYGB7cn0Kc3VtbWFyeShtZXRhZGF0cmV2aWV3JEV4cGVyaW1lbnRPYnNlcnZhdGlvbikKYGBgCgoKCiMjIFdoYXQgdHlwZXMgb2YgZWNvc3lzdGVtcy9sYW5kIGNvdmVycyBhcmUgcmVwcmVzZW50ZWQ/IApgYGB7cn0Kd2hpY2hTeXN0ZW0gPC0gYygiR3Jhc3NsYW5kIiwiV29vZGxhbmQiLCJDcm9wbGFuZCIsIldldGxhbmQiLCJBcnRpZmljYWwiLCJCYXJlIGxhbmQiLCJTaHJ1YmxhbmQiLCJPdGhlcnMiKQoKaXRlcmF0aW9ucyA9IGxlbmd0aCh3aGljaFN5c3RlbSkKdmFyaWFibGVzID0gMQoKb3V0cHV0IDwtIG1hdHJpeChuY29sPXZhcmlhYmxlcywgbnJvdz1pdGVyYXRpb25zKQoKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICBvdXRwdXRbaSxdIDwtIGxlbmd0aChncmVwKHdoaWNoU3lzdGVtW2ldLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGFzLmNoYXJhY3RlcihtZXRhZGF0cmV2aWV3JFN5c3RlbSkpKQp9CgpvdXRwdXQgPC0gZGF0YS5mcmFtZShvdXRwdXQpCm91dHB1dCRTeXN0ZW0gPC0gd2hpY2hTeXN0ZW0Kb3V0cHV0JG5vLnN0dWRpZXMgPC0gb3V0cHV0JG91dHB1dApvdXRwdXQkU3lzdGVtMiA8LSByZW9yZGVyKG91dHB1dCRTeXN0ZW0sIG91dHB1dCRvdXRwdXQpCiAKCgpnZ3Bsb3Qob3V0cHV0LCBhZXMoeCA9IFN5c3RlbTIsIHkgPSBuby5zdHVkaWVzKSkrCiAgZ2VvbV9jb2woKSsKICAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSByZWwoMikpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gcmVsKDIpKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgCiAgICAgICAgI2xlZ2VuZAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSAsCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksIAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIAogICAgICAgICNmb3IgdGhlIGZhY2V0cwogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIpKQoKCmBgYAoKCgoKCiMjIyBDaXRhdGlvbnMgb2YgcGFwZXJzIHRoYXQgYWRkcmVzc2VkIGJpb2luZGljYXRvcnMKCmBgYHtyfQoKIyAjIDIyIHN0dWRpZXMgaGF2ZSBiaW9pbmRpY2F0b3IgaW4gdGhlIGFic3RyYWN0CndoaWNoS2V5d29yZHMgPC0gYygiYmlvaW5kaWNhdG9yfGJpby1pbmRpY2F0b3IiKQoKCml0ZXJhdGlvbnMgPSBsZW5ndGgod2hpY2hLZXl3b3JkcykKdmFyaWFibGVzID0gMQoKb3V0cHV0IDwtIG1hdHJpeChuY29sPXZhcmlhYmxlcywgbnJvdz1pdGVyYXRpb25zKQoKZm9yKGkgaW4gMTppdGVyYXRpb25zKXsKICBvdXRwdXRbaSxdIDwtIGxlbmd0aChncmVwKHdoaWNoS2V5d29yZHNbaV0sIGlnbm9yZS5jYXNlID0gVFJVRSwgYXMuY2hhcmFjdGVyKG1ldGFkYXRyZXZpZXckYWJzdHJhY3QpKSkKfQoKb3V0cHV0IDwtIGRhdGEuZnJhbWUob3V0cHV0KQpvdXRwdXQkS2V5d29yZCA8LSB3aGljaEtleXdvcmRzCm91dHB1dAoKIyBsaXN0IHRob3NlIHBhcGVycwpCaW9JbmRpY1N0dWRpZXNBYnN0cmFjdCA8LSBtZXRhZGF0cmV2aWV3W2dyZXBsKCJiaW9pbmRpY2F0b3IiLCBtZXRhZGF0cmV2aWV3JGFic3RyYWN0KSxdCkJpb0luZGljU3R1ZGllc1RpdGxlIDwtIG1ldGFkYXRyZXZpZXdbZ3JlcGwoImJpb2luZGljYXRvciIsIG1ldGFkYXRyZXZpZXckdGl0bGUpLF0KQmlvSW5kaWNTdHVkaWVzVGl0bGUKYGBgCgoKIyMgU3VwcGxlbWVudCBUYWJsZXMKCiMjIyBTdXBwbGVtZW50IFRhYmxlOiBhbGwgcGFwZXJzIHdpdGggbXVsdGl0cm9waGljLWRpdmVyc2l0eQoKVGhlIGxpc3Qgb2YgcGFwZXJzIGZvY3VzaW5nIG9uIHBsYW50cywgbWljcm9iZXMgYW5kIHNvaWwgZmF1bmEgd2FzIHVzZWQgdG8gaW52ZXN0aWdhdGUgd2hldGhlciB0aGUgc3R1ZGllcyBhZGRyZXNzZWQgaW5kaXJlY3QgZWZmZWN0cyBvZiBjaGVtaWNhbCBzdHJlc3NvcnMsIGFuZCBpZiB0aGV5IGNvbnNpZGVyZWQgZm9vZHdlYiBvciBuZXR3b3JrIGFwcHJvYWNoZXMuIAoKYGBge3J9CiMgY3JlYXRlIGEgdGFibGUgd2l0aCBhbGwgc3R1ZGllcyB0aGF0IGhhdmUgYXQgbGVhc3Qgb25lIEVGCldoaWNoVmFycyA8LSBjKCJhdXRob3JzIiwgIlB1Ymxpc2hlZC5ZZWFyIiwgImpvdXJuYWwiLCAidGl0bGUiLCAiRE9JIiwgIkNvdW50cnkiLCAiRGl2ZXJzaXR5TWV0cmljIiwgIlBvbGx1dGFudE5hbWUiLCAiUG9sbHV0YW50VHlwZSIsICJQb2xsdXRpb25Tb3VyY2UiLCAiVGF4YUdyb3VwQXRsYXMiLCAiU3lzdGVtIiwgIk90aGVyQmlvZGl2ZXJzaXR5IiwgIkVjb3N5c3RlbUZ1bmN0aW9uIiwgIk11bHRpZHJpdmVycyIpCgpNQmRhdGEgPC0gbWV0YWRhdHJldmlld1ttZXRhZGF0cmV2aWV3JE90aGVyQmlvZGl2ZXJzaXR5ICE9ICJubyIsV2hpY2hWYXJzXQoKCndyaXRlLmNzdihNQmRhdGEsICJPdXRwdXQvVGFibGVQYXBlcnNXaXRoTXVsdGlEaXYuY3N2IikKYGBgCgojIyMgU3VwcGxlbWVudCBUYWJsZTogYWxsIHBhcGVycyB3aXRoIGZ1bmN0aW9ucwoKVGhlIGxpc3Qgb2YgcGFwZXJzIGhhdmluZyBhdCBsZWFzdCBvbmUgZnVuY3Rpb24gd2FzIHVzZWQgdG8gaW52ZXN0aWdhdGUgd2hhdCB0eXBlIG9mIGZ1bmN0aW9ucyBhbmQgZWNvc3lzdGVtIHNlcnZpY2VzIHdlcmUgYWRkcmVzc2VkIGluIHRob3NlIHN0dWRpZXMuCgpgYGB7cn0KIyBjcmVhdGUgYSB0YWJsZSB3aXRoIGFsbCBzdHVkaWVzIHRoYXQgaGF2ZSBhdCBsZWFzdCBvbmUgRUYKV2hpY2hWYXJzIDwtIGMoImF1dGhvcnMiLCAiUHVibGlzaGVkLlllYXIiLCAiam91cm5hbCIsICJ0aXRsZSIsICJET0kiLCAiQ291bnRyeSIsICJEaXZlcnNpdHlNZXRyaWMiLCAiUG9sbHV0YW50TmFtZSIsICJQb2xsdXRhbnRUeXBlIiwgIlBvbGx1dGlvblNvdXJjZSIsICJUYXhhR3JvdXBBdGxhcyIsICJTeXN0ZW0iLCAiT3RoZXJCaW9kaXZlcnNpdHkiLCAiRWNvc3lzdGVtRnVuY3Rpb24iLCAiTXVsdGlkcml2ZXJzIikKCkVGZGF0YSA8LSBtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckRWNvc3lzdGVtRnVuY3Rpb24gIT0gIm5vbmUiLFdoaWNoVmFyc10KCgp3cml0ZS5jc3YoRUZkYXRhLCAiT3V0cHV0L1RhYmxlUGFwZXJzV2l0aEVGLmNzdiIpCmBgYAoKIyMjIFN1cHBsZW1lbnQgVGFibGU6IGFsbCBwYXBlcnMgd2l0aCBtdWx0aS1kcml2ZXJzCgpUaGUgbGlzdCBvZiBwYXBlcnMgaGF2aW5nIG11bHRpcGxlIGdsb2JhbCBjaGFuZ2UgZHJpdmVycyB3YXMgdXNlZCB0byBpbnZlc3RpZ2F0ZSBob3cgb2Z0ZW4gc3R1ZGllcyBoYWQgYSBmdWxsIGZhY3RvcmlhbCBkZXNpZ24gZW5hYmxpbmcgdG8gYWRkcmVzcyBpbnRlcmFjdGl2ZSBlZmZlY3RzLiAKCmBgYHtyfQpXaGljaFZhcnMgPC0gYygiYXV0aG9ycyIsICJQdWJsaXNoZWQuWWVhciIsICJqb3VybmFsIiwgInRpdGxlIiwgIkRPSSIsICJDb3VudHJ5IiwgIkRpdmVyc2l0eU1ldHJpYyIsICJQb2xsdXRhbnROYW1lIiwgIlBvbGx1dGFudFR5cGUiLCAiUG9sbHV0aW9uU291cmNlIiwgIlRheGFHcm91cEF0bGFzIiwgIlN5c3RlbSIsICJPdGhlckJpb2RpdmVyc2l0eSIsICJFY29zeXN0ZW1GdW5jdGlvbiIsICJNdWx0aWRyaXZlcnMiLAogICAgICAgICAgICAgICAiQ2xpbWF0ZUNoYW5nZSIsICJOdXRyaWVudCIsICJMYW5kVXNlIiwKICAgICAgICAgICAgICAgIkludGVuc2lmaWNhdGlvbiIsICJGcmFnbWVudGF0aW9uLkxvc3MiLCAiSW52YXNpdmVzIikKCk11bHRpR0NEZGF0YSA8LSBtZXRhZGF0cmV2aWV3W21ldGFkYXRyZXZpZXckTXVsdGlkcml2ZXJzICE9ICJubyIsV2hpY2hWYXJzXQoKCndyaXRlLmNzdihNdWx0aUdDRGRhdGEsICJPdXRwdXQvVGFibGVQYXBlcnNXaXRoTXVsdGlHQ0RzLmNzdiIpCmBgYAo=